libraries

#graph plotting packages.
library(data.table)
library(ggplot2)
library(plotly)
library(gganimate)
library(d3Tree)
library(dplyr)
library(mltools)
library(corrplot)
library(kableExtra)

Reading the data into R and dropping Over18,EmployeeCount,EmployeeNumber,StandardHours because in these columns the data is either unique or the data is same for all the row in the table.

hr<-fread("WA_Fn-UseC_-HR-Employee-Attrition.csv",na.strings = "NA",stringsAsFactors = TRUE,drop = c("Over18","EmployeeCount","EmployeeNumber","StandardHours"))

Now seing the structure of the data to get some idea of type of data present in the data set.

str(hr)
Classes ‘data.table’ and 'data.frame':  1470 obs. of  31 variables:
 $ Age                     : int  41 49 37 33 27 32 59 30 38 36 ...
 $ Attrition               : Factor w/ 2 levels "No","Yes": 2 1 2 1 1 1 1 1 1 1 ...
 $ BusinessTravel          : Factor w/ 3 levels "Non-Travel","Travel_Frequently",..: 3 2 3 2 3 2 3 3 2 3 ...
 $ DailyRate               : int  1102 279 1373 1392 591 1005 1324 1358 216 1299 ...
 $ Department              : Factor w/ 3 levels "Human Resources",..: 3 2 2 2 2 2 2 2 2 2 ...
 $ DistanceFromHome        : int  1 8 2 3 2 2 3 24 23 27 ...
 $ Education               : int  2 1 2 4 1 2 3 1 3 3 ...
 $ EducationField          : Factor w/ 6 levels "Human Resources",..: 2 2 5 2 4 2 4 2 2 4 ...
 $ EnvironmentSatisfaction : int  2 3 4 4 1 4 3 4 4 3 ...
 $ Gender                  : Factor w/ 2 levels "Female","Male": 1 2 2 1 2 2 1 2 2 2 ...
 $ HourlyRate              : int  94 61 92 56 40 79 81 67 44 94 ...
 $ JobInvolvement          : int  3 2 2 3 3 3 4 3 2 3 ...
 $ JobLevel                : int  2 2 1 1 1 1 1 1 3 2 ...
 $ JobRole                 : Factor w/ 9 levels "Healthcare Representative",..: 8 7 3 7 3 3 3 3 5 1 ...
 $ JobSatisfaction         : int  4 2 3 3 2 4 1 3 3 3 ...
 $ MaritalStatus           : Factor w/ 3 levels "Divorced","Married",..: 3 2 3 2 2 3 2 1 3 2 ...
 $ MonthlyIncome           : int  5993 5130 2090 2909 3468 3068 2670 2693 9526 5237 ...
 $ MonthlyRate             : int  19479 24907 2396 23159 16632 11864 9964 13335 8787 16577 ...
 $ NumCompaniesWorked      : int  8 1 6 1 9 0 4 1 0 6 ...
 $ OverTime                : Factor w/ 2 levels "No","Yes": 2 1 2 2 1 1 2 1 1 1 ...
 $ PercentSalaryHike       : int  11 23 15 11 12 13 20 22 21 13 ...
 $ PerformanceRating       : int  3 4 3 3 3 3 4 4 4 3 ...
 $ RelationshipSatisfaction: int  1 4 2 3 4 3 1 2 2 2 ...
 $ StockOptionLevel        : int  0 1 0 0 1 0 3 1 0 2 ...
 $ TotalWorkingYears       : int  8 10 7 8 6 8 12 1 10 17 ...
 $ TrainingTimesLastYear   : int  0 3 3 3 3 2 3 2 2 3 ...
 $ WorkLifeBalance         : int  1 3 3 3 3 2 2 3 3 2 ...
 $ YearsAtCompany          : int  6 10 0 8 2 7 1 1 9 7 ...
 $ YearsInCurrentRole      : int  4 7 0 7 2 7 0 0 7 7 ...
 $ YearsSinceLastPromotion : int  0 1 0 3 2 3 0 0 1 7 ...
 $ YearsWithCurrManager    : int  5 7 0 0 2 6 0 0 8 7 ...
 - attr(*, ".internal.selfref")=<externalptr> 

Now viewing the summary of all numeric column of the data set.

summary(hr)
      Age        Attrition            BusinessTravel   DailyRate                       Department  DistanceFromHome   Education              EducationField
 Min.   :18.00   No :1233   Non-Travel       : 150   Min.   : 102.0   Human Resources       : 63   Min.   : 1.000   Min.   :1.000   Human Resources : 27   
 1st Qu.:30.00   Yes: 237   Travel_Frequently: 277   1st Qu.: 465.0   Research & Development:961   1st Qu.: 2.000   1st Qu.:2.000   Life Sciences   :606   
 Median :36.00              Travel_Rarely    :1043   Median : 802.0   Sales                 :446   Median : 7.000   Median :3.000   Marketing       :159   
 Mean   :36.92                                       Mean   : 802.5                                Mean   : 9.193   Mean   :2.913   Medical         :464   
 3rd Qu.:43.00                                       3rd Qu.:1157.0                                3rd Qu.:14.000   3rd Qu.:4.000   Other           : 82   
 Max.   :60.00                                       Max.   :1499.0                                Max.   :29.000   Max.   :5.000   Technical Degree:132   
                                                                                                                                                           
 EnvironmentSatisfaction    Gender      HourlyRate     JobInvolvement    JobLevel                          JobRole    JobSatisfaction  MaritalStatus
 Min.   :1.000           Female:588   Min.   : 30.00   Min.   :1.00   Min.   :1.000   Sales Executive          :326   Min.   :1.000   Divorced:327  
 1st Qu.:2.000           Male  :882   1st Qu.: 48.00   1st Qu.:2.00   1st Qu.:1.000   Research Scientist       :292   1st Qu.:2.000   Married :673  
 Median :3.000                        Median : 66.00   Median :3.00   Median :2.000   Laboratory Technician    :259   Median :3.000   Single  :470  
 Mean   :2.722                        Mean   : 65.89   Mean   :2.73   Mean   :2.064   Manufacturing Director   :145   Mean   :2.729                 
 3rd Qu.:4.000                        3rd Qu.: 83.75   3rd Qu.:3.00   3rd Qu.:3.000   Healthcare Representative:131   3rd Qu.:4.000                 
 Max.   :4.000                        Max.   :100.00   Max.   :4.00   Max.   :5.000   Manager                  :102   Max.   :4.000                 
                                                                                      (Other)                  :215                                 
 MonthlyIncome    MonthlyRate    NumCompaniesWorked OverTime   PercentSalaryHike PerformanceRating RelationshipSatisfaction StockOptionLevel TotalWorkingYears
 Min.   : 1009   Min.   : 2094   Min.   :0.000      No :1054   Min.   :11.00     Min.   :3.000     Min.   :1.000            Min.   :0.0000   Min.   : 0.00    
 1st Qu.: 2911   1st Qu.: 8047   1st Qu.:1.000      Yes: 416   1st Qu.:12.00     1st Qu.:3.000     1st Qu.:2.000            1st Qu.:0.0000   1st Qu.: 6.00    
 Median : 4919   Median :14236   Median :2.000                 Median :14.00     Median :3.000     Median :3.000            Median :1.0000   Median :10.00    
 Mean   : 6503   Mean   :14313   Mean   :2.693                 Mean   :15.21     Mean   :3.154     Mean   :2.712            Mean   :0.7939   Mean   :11.28    
 3rd Qu.: 8379   3rd Qu.:20462   3rd Qu.:4.000                 3rd Qu.:18.00     3rd Qu.:3.000     3rd Qu.:4.000            3rd Qu.:1.0000   3rd Qu.:15.00    
 Max.   :19999   Max.   :26999   Max.   :9.000                 Max.   :25.00     Max.   :4.000     Max.   :4.000            Max.   :3.0000   Max.   :40.00    
                                                                                                                                                              
 TrainingTimesLastYear WorkLifeBalance YearsAtCompany   YearsInCurrentRole YearsSinceLastPromotion YearsWithCurrManager
 Min.   :0.000         Min.   :1.000   Min.   : 0.000   Min.   : 0.000     Min.   : 0.000          Min.   : 0.000      
 1st Qu.:2.000         1st Qu.:2.000   1st Qu.: 3.000   1st Qu.: 2.000     1st Qu.: 0.000          1st Qu.: 2.000      
 Median :3.000         Median :3.000   Median : 5.000   Median : 3.000     Median : 1.000          Median : 3.000      
 Mean   :2.799         Mean   :2.761   Mean   : 7.008   Mean   : 4.229     Mean   : 2.188          Mean   : 4.123      
 3rd Qu.:3.000         3rd Qu.:3.000   3rd Qu.: 9.000   3rd Qu.: 7.000     3rd Qu.: 3.000          3rd Qu.: 7.000      
 Max.   :6.000         Max.   :4.000   Max.   :40.000   Max.   :18.000     Max.   :15.000          Max.   :17.000      
                                                                                                                       
colnames(hr)
 [1] "Age"                      "Attrition"                "BusinessTravel"           "DailyRate"                "Department"              
 [6] "DistanceFromHome"         "Education"                "EducationField"           "EnvironmentSatisfaction"  "Gender"                  
[11] "HourlyRate"               "JobInvolvement"           "JobLevel"                 "JobRole"                  "JobSatisfaction"         
[16] "MaritalStatus"            "MonthlyIncome"            "MonthlyRate"              "NumCompaniesWorked"       "OverTime"                
[21] "PercentSalaryHike"        "PerformanceRating"        "RelationshipSatisfaction" "StockOptionLevel"         "TotalWorkingYears"       
[26] "TrainingTimesLastYear"    "WorkLifeBalance"          "YearsAtCompany"           "YearsInCurrentRole"       "YearsSinceLastPromotion" 
[31] "YearsWithCurrManager"    

Now we are grouping the ages bases based on age groups. 20-belowe 20-30 30-40 40-50 50-60 so now we are dividing our analysis on different age group.

hr[,Age:=as.factor( ifelse(
  Age <= 20 , '15-20',
    ifelse(Age >20  & Age <= 30,'20-30' ,
      ifelse(Age > 30  & Age <= 40, '30-40', 
          ifelse(Age >40 & Age <=50,'40-50','50-60')   )
    )
  )
)
]
colnames(hr)
 [1] "Age"                      "Attrition"                "BusinessTravel"           "DailyRate"                "Department"              
 [6] "DistanceFromHome"         "Education"                "EducationField"           "EnvironmentSatisfaction"  "Gender"                  
[11] "HourlyRate"               "JobInvolvement"           "JobLevel"                 "JobRole"                  "JobSatisfaction"         
[16] "MaritalStatus"            "MonthlyIncome"            "MonthlyRate"              "NumCompaniesWorked"       "OverTime"                
[21] "PercentSalaryHike"        "PerformanceRating"        "RelationshipSatisfaction" "StockOptionLevel"         "TotalWorkingYears"       
[26] "TrainingTimesLastYear"    "WorkLifeBalance"          "YearsAtCompany"           "YearsInCurrentRole"       "YearsSinceLastPromotion" 
[31] "YearsWithCurrManager"    

In our data we have some features “Education”, “EnvironmentSatisfaction”,“JobInvolvement”,“JobLevel”,“JobSatisfaction”,“PerformanceRating”,“RelationshipSatisfaction”,“StockOptionLevel”," WorkLifeBalance" which are numeric but the sense they make is factors,so now converting those variable to factors.

fact_columns<-c("Education", "EnvironmentSatisfaction","JobInvolvement","JobLevel","JobSatisfaction","PerformanceRating","RelationshipSatisfaction","StockOptionLevel","WorkLifeBalance")
hr<-as.data.frame(hr)
hr[fact_columns]<-lapply(hr[fact_columns], as.factor)
hr<-as.data.table(hr)

Analysis on age group.

Now we are comparing which age groups are travelling the most.

ggplot(hr[,.N,by=list(Age,BusinessTravel,Gender)],aes(x=Age,y=N,fill=Gender))+geom_bar(stat = "identity",width = 0.6,position = "dodge")+facet_grid(~BusinessTravel)+geom_text(aes(label=N), vjust=-0.3)+labs(x ="The Age Groups", y = "Number of Employee")

salary_monthly_dep<-function(age,typeTravel){
x<-ggplot(hr[Age==age& BusinessTravel==typeTravel,.N,by=list(JobRole,JobSatisfaction,MonthlyIncome, Department)],aes(x=JobRole,y=MonthlyIncome,fill=JobRole))+geom_bar(stat="identity",width = 0.3) +facet_wrap(~Department,ncol = 1,scales = "free")
ggplotly(x)
}
salary_monthly_dep("30-40","Travel_Rarely")
#for movement
#transition_states(EnvironmentSatisfaction, .01, .001)+ease_aes('cubic-in-out')
#for plotting multiplot on one page 
#multiplot(p1, p2, p3, p4, cols=2)

Now finding the insights regarding employee turn over. so we found few feature which are very important for employee turnover. MonthlyIncome ,TotalWorkingYears,OverTime ,JobLevel JobRole ,Age, EnvironmentSatisfaction ,YearsAtCompany

what percentage of employees leaving the company.

# Create test data.
dat = data.frame(count=c(10, 60, 30), category=c("A", "B", "C"))
 
# Add addition columns, needed for drawing with geom_rect.
dat$fraction = dat$count / sum(dat$count)
dat = dat[order(dat$fraction), ]
dat$ymax = cumsum(dat$fraction)
dat$ymin = c(0, head(dat$ymax, n=-1))
 
# Make the plot
p1 = ggplot(dat, aes(fill=category, ymax=ymax, ymin=ymin, xmax=4, xmin=3)) +
     geom_rect() +
     coord_polar(theta="y") +
     xlim(c(0, 4)) +
     theme(panel.grid=element_blank()) +
     theme(axis.text=element_blank()) +
     theme(axis.ticks=element_blank()) +
     annotate("text", x = 0, y = 0, label = "My Ring plot !") +
     labs(title="")

Now ploting the scatter plot for to see the relationship between the monthly income ,Total working years and the attrition rate.

x<-ggplot(hr,aes(TotalWorkingYears,MonthlyIncome,color=JobLevel,shape=OverTime))+geom_point()+facet_wrap(~Attrition)+labs(x = "Total Working Years",y="Monthly_Income")
ggplotly(x)

As we see in the above graph that the employees who ’s monthly income is less than 10000$ ,total working years in range 0-20 ,job level 1-3 are having the maximum attrition.

so now i am dividing my analysis into 2 parts as in monthly_income less than 5000 and 5000-10000 different as different job level people are :- 1)analysis on employee who ’s monthly income is less than 5000$ 2)analysis on employee who ’s monthly income is in between 5000$ - 10000$

Now doing analysis on the employees who ’s salary is less than 5000$ Now we further dividing our analysis in to two parts total working years 1) Less than 5 2) Between 5 to 10 yeras

Now I am going to view job level of employee and wheather the employee is working over time ,of those employee ’s who ’s monthly income is less than 5000$ and total woking years is in the range 0-5 years.

x<-ggplot(hr[MonthlyIncome<=5000 & TotalWorkingYears <= 5 & Attrition=="Yes",.N,by=list(JobLevel,OverTime,MonthlyIncome,TotalWorkingYears)],aes(TotalWorkingYears,MonthlyIncome,color=JobLevel))+geom_point()+facet_wrap(~OverTime)+labs(x = "Total Working Years",y="Monthly_Income")+labs(title = "Over Time",fill="Age Group")
ggplotly(x)

so by seing the above graph we can infer that job level 1 and 2 employee ’s who ’s salary is less than 5000$ and the total working years is less than 5 have high probability of leaving the company,their is no much difference of employee turnover in these employees

But if we see in more detail 1 year experience and job level 1 employee are leaving the most.

Now we are checking their Environment satisfaction rating ,job,roles,Age group.

x<-ggplot(hr[MonthlyIncome <= 5000 & TotalWorkingYears <= 1 & JobLevel == 1  &Attrition=="Yes",.N,by=list(JobRole,EnvironmentSatisfaction,Age)],aes(x=JobRole,y=N,fill=Age))+geom_bar(stat="identity",width = 0.3) +facet_wrap(~EnvironmentSatisfaction ,ncol = 1,scales = "free")+coord_flip()+labs(title = "EnvironmentSatisfaction Rating (1-4)",y="The Number of Employees moving out of the company. ",fill="Age Group")
ggplotly(x)

By the above graph we can infer by job role that

1) research scientist

There are total 12 employees where

  1. 3 have rated EnvironmentSatisfaction Low ,3/12 = 25%
  2. 1 have rated EnvironmentSatisfaction High ,1/12 = 0.08%
  3. 8 have rated EnvironmentSatisfaction very High = 66%

At the age group of 30-40 the employee was not satisfied.At the age group of 15-20 the employee are very much satisfied with the Environment but still leaving the company.

so now we are going to find out why at age group of 15-20 ,who ’s environment satisfaction is high and very high are still leaving the company.

The maximum monthly income of one year experience research Scientist by the historical data of the company is : 2994 The minimum monthly income of one year experience research scientist by the historical data of the company is : 1009

The maximum monthly income of research Scientist by the historical data of the company is :9724 The minimum monthly income of research scientist by the historical data of the company is :1009

x<-ggplot(hr[MonthlyIncome<=5000 & TotalWorkingYears == 1 &Attrition=="Yes"& (EnvironmentSatisfaction==3 | EnvironmentSatisfaction==4)&JobRole=="Research Scientist",.N,by=list(OverTime,MonthlyIncome,YearsAtCompany,WorkLifeBalance)],aes(YearsAtCompany,MonthlyIncome,fill= WorkLifeBalance ))+geom_point()+facet_wrap(~OverTime)+labs(x = "",y="Monthly_Income")+labs(title = "Over Time",x="years at the company",fill="Work Life Balance")
ggplotly(x)

The most of the employee who came to this post in this company are freshers and they got 1 year of experience in the current company. The research scientist who are doing over time and getting the similar monthly income as the employee who are not doing over time are leaving the company. their work life balance is good.

x<-ggplot(hr[MonthlyIncome<=5000 & TotalWorkingYears == 1 &Attrition=="Yes"& (EnvironmentSatisfaction==3 | EnvironmentSatisfaction==4)&JobRole=="Research Scientist"&OverTime=="Yes",.N,by=list(BusinessTravel,StockOptionLevel ,DistanceFromHome ,YearsWithCurrManager,  JobInvolvement ,PercentSalaryHike,RelationshipSatisfaction, EducationField)],aes(PercentSalaryHike,RelationshipSatisfaction ,fill=  JobInvolvement  ))+geom_point()+facet_wrap(~EducationField)+labs(x = "PercentSalaryHike",y="RelationshipSatisfaction ")+labs(title = "EducationField",fill="JobInvolvement")
ggplotly(x)

The employees who are frequently travelling the bussiness trips are generally moving out of the company and their job involment is also medium.Their relationshipsatisfaction rating also is very low

The minimum percentage hikes given to research scientist is : 11% The maximum percentage hikes given to research scientist is : 25%

so the percentage hikes are also given good to these employees.

x<-ggplot(hr[MonthlyIncome <= 5000 & TotalWorkingYears <= 5 & (JobLevel == 2 |  JobLevel == 1)  &Attrition=="Yes",.N,by=list(JobRole,Department,Attrition,EnvironmentSatisfaction,Age)],aes(x=JobRole,y=N,fill=Age))+geom_bar(stat="identity",width = 0.3) +facet_wrap(~EnvironmentSatisfaction ,ncol = 1,scales = "free")+coord_flip()+labs(title = "EnvironmentSatisfaction Rating (1-4)",y="The Number of Employees moving out of the company. ",fill="Age Group")
ggplotly(x)

As we can see in the above graph we can infer that For the employee ’s who ’s monthly income is below 5000$ ,total working years is less than 5 ,we have only two job levels

For job level 1 these are the job roles and department : 1:Laboratory Technician (Research & Development department) 2:Research Scientist (Research & Development department) 3:Sales Representative (Sales department) 4:Human Resources (Human Resources department)

For job level 2 these are the job roles and department : 1:Manufacturing Director (Research & Development department) 2:Sales Executive (Sales department) 3:Healthcare Representative (Research & Development department) 4:Sales Representative (Sales department) 5:Laboratory Technician (Research & Development department)

By seing the EnvironmentSatisfaction rating of the employee who are leaving the company we can infer that :

At Laboratory Technician job role we have very high number of employees turnover,their Environment satisfaction rating is low in all the age group ’s of the employee.

We can see from the above graph in job role of Sales Representative(Sales department),Research Scientist (Research & Development department),Laboratory Technician (Research & Development department) these employee are leaving the company in a larger scale as compare to the other job roles..

Age YearsAtCompany EnvironmentSatisfaction MaritalStatus

Now I am going to look age,yearsAtcompany,Environmentsatisfaction,MaritalStatus.of the employees who ’s job role are Sales Representative(Sales department),Research Scientist (Research & Development department),Laboratory Technician (Research & Development department)

nn <- hr[,list(Age,YearsAtCompany,EnvironmentSatisfaction,MaritalStatus)]
d3tree(list(root = df2tree(rootname = 'Overall',struct = as.data.frame(nn)),
   layout = 'collapse'))
str(hr)
Classes ‘data.table’ and 'data.frame':  1470 obs. of  31 variables:
 $ Age                     : Factor w/ 5 levels "15-20","20-30",..: 4 4 3 3 2 3 5 2 3 3 ...
 $ Attrition               : Factor w/ 2 levels "No","Yes": 2 1 2 1 1 1 1 1 1 1 ...
 $ BusinessTravel          : Factor w/ 3 levels "Non-Travel","Travel_Frequently",..: 3 2 3 2 3 2 3 3 2 3 ...
 $ DailyRate               : int  1102 279 1373 1392 591 1005 1324 1358 216 1299 ...
 $ Department              : Factor w/ 3 levels "Human Resources",..: 3 2 2 2 2 2 2 2 2 2 ...
 $ DistanceFromHome        : int  1 8 2 3 2 2 3 24 23 27 ...
 $ Education               : Factor w/ 5 levels "1","2","3","4",..: 2 1 2 4 1 2 3 1 3 3 ...
 $ EducationField          : Factor w/ 6 levels "Human Resources",..: 2 2 5 2 4 2 4 2 2 4 ...
 $ EnvironmentSatisfaction : Factor w/ 4 levels "1","2","3","4": 2 3 4 4 1 4 3 4 4 3 ...
 $ Gender                  : Factor w/ 2 levels "Female","Male": 1 2 2 1 2 2 1 2 2 2 ...
 $ HourlyRate              : int  94 61 92 56 40 79 81 67 44 94 ...
 $ JobInvolvement          : Factor w/ 4 levels "1","2","3","4": 3 2 2 3 3 3 4 3 2 3 ...
 $ JobLevel                : Factor w/ 5 levels "1","2","3","4",..: 2 2 1 1 1 1 1 1 3 2 ...
 $ JobRole                 : Factor w/ 9 levels "Healthcare Representative",..: 8 7 3 7 3 3 3 3 5 1 ...
 $ JobSatisfaction         : Factor w/ 4 levels "1","2","3","4": 4 2 3 3 2 4 1 3 3 3 ...
 $ MaritalStatus           : Factor w/ 3 levels "Divorced","Married",..: 3 2 3 2 2 3 2 1 3 2 ...
 $ MonthlyIncome           : int  5993 5130 2090 2909 3468 3068 2670 2693 9526 5237 ...
 $ MonthlyRate             : int  19479 24907 2396 23159 16632 11864 9964 13335 8787 16577 ...
 $ NumCompaniesWorked      : int  8 1 6 1 9 0 4 1 0 6 ...
 $ OverTime                : Factor w/ 2 levels "No","Yes": 2 1 2 2 1 1 2 1 1 1 ...
 $ PercentSalaryHike       : int  11 23 15 11 12 13 20 22 21 13 ...
 $ PerformanceRating       : Factor w/ 2 levels "3","4": 1 2 1 1 1 1 2 2 2 1 ...
 $ RelationshipSatisfaction: Factor w/ 4 levels "1","2","3","4": 1 4 2 3 4 3 1 2 2 2 ...
 $ StockOptionLevel        : Factor w/ 4 levels "0","1","2","3": 1 2 1 1 2 1 4 2 1 3 ...
 $ TotalWorkingYears       : int  8 10 7 8 6 8 12 1 10 17 ...
 $ TrainingTimesLastYear   : int  0 3 3 3 3 2 3 2 2 3 ...
 $ WorkLifeBalance         : Factor w/ 4 levels "1","2","3","4": 1 3 3 3 3 2 2 3 3 2 ...
 $ YearsAtCompany          : int  6 10 0 8 2 7 1 1 9 7 ...
 $ YearsInCurrentRole      : int  4 7 0 7 2 7 0 0 7 7 ...
 $ YearsSinceLastPromotion : int  0 1 0 3 2 3 0 0 1 7 ...
 $ YearsWithCurrManager    : int  5 7 0 0 2 6 0 0 8 7 ...
 - attr(*, ".internal.selfref")=<externalptr> 
str(hr)
Classes ‘data.table’ and 'data.frame':  1470 obs. of  31 variables:
 $ Age                     : int  41 49 37 33 27 32 59 30 38 36 ...
 $ Attrition               : Factor w/ 2 levels "No","Yes": 2 1 2 1 1 1 1 1 1 1 ...
 $ BusinessTravel          : Factor w/ 3 levels "Non-Travel","Travel_Frequently",..: 3 2 3 2 3 2 3 3 2 3 ...
 $ DailyRate               : int  1102 279 1373 1392 591 1005 1324 1358 216 1299 ...
 $ Department              : Factor w/ 3 levels "Human Resources",..: 3 2 2 2 2 2 2 2 2 2 ...
 $ DistanceFromHome        : int  1 8 2 3 2 2 3 24 23 27 ...
 $ Education               : Factor w/ 5 levels "1","2","3","4",..: 2 1 2 4 1 2 3 1 3 3 ...
 $ EducationField          : Factor w/ 6 levels "Human Resources",..: 2 2 5 2 4 2 4 2 2 4 ...
 $ EnvironmentSatisfaction : Factor w/ 4 levels "1","2","3","4": 2 3 4 4 1 4 3 4 4 3 ...
 $ Gender                  : Factor w/ 2 levels "Female","Male": 1 2 2 1 2 2 1 2 2 2 ...
 $ HourlyRate              : int  94 61 92 56 40 79 81 67 44 94 ...
 $ JobInvolvement          : Factor w/ 4 levels "1","2","3","4": 3 2 2 3 3 3 4 3 2 3 ...
 $ JobLevel                : Factor w/ 5 levels "1","2","3","4",..: 2 2 1 1 1 1 1 1 3 2 ...
 $ JobRole                 : Factor w/ 9 levels "Healthcare Representative",..: 8 7 3 7 3 3 3 3 5 1 ...
 $ JobSatisfaction         : Factor w/ 4 levels "1","2","3","4": 4 2 3 3 2 4 1 3 3 3 ...
 $ MaritalStatus           : Factor w/ 3 levels "Divorced","Married",..: 3 2 3 2 2 3 2 1 3 2 ...
 $ MonthlyIncome           : int  5993 5130 2090 2909 3468 3068 2670 2693 9526 5237 ...
 $ MonthlyRate             : int  19479 24907 2396 23159 16632 11864 9964 13335 8787 16577 ...
 $ NumCompaniesWorked      : int  8 1 6 1 9 0 4 1 0 6 ...
 $ OverTime                : Factor w/ 2 levels "No","Yes": 2 1 2 2 1 1 2 1 1 1 ...
 $ PercentSalaryHike       : int  11 23 15 11 12 13 20 22 21 13 ...
 $ PerformanceRating       : Factor w/ 2 levels "3","4": 1 2 1 1 1 1 2 2 2 1 ...
 $ RelationshipSatisfaction: Factor w/ 4 levels "1","2","3","4": 1 4 2 3 4 3 1 2 2 2 ...
 $ StockOptionLevel        : Factor w/ 4 levels "0","1","2","3": 1 2 1 1 2 1 4 2 1 3 ...
 $ TotalWorkingYears       : int  8 10 7 8 6 8 12 1 10 17 ...
 $ TrainingTimesLastYear   : int  0 3 3 3 3 2 3 2 2 3 ...
 $ WorkLifeBalance         : Factor w/ 4 levels "1","2","3","4": 1 3 3 3 3 2 2 3 3 2 ...
 $ YearsAtCompany          : int  6 10 0 8 2 7 1 1 9 7 ...
 $ YearsInCurrentRole      : int  4 7 0 7 2 7 0 0 7 7 ...
 $ YearsSinceLastPromotion : int  0 1 0 3 2 3 0 0 1 7 ...
 $ YearsWithCurrManager    : int  5 7 0 0 2 6 0 0 8 7 ...
 - attr(*, ".internal.selfref")=<externalptr> 
convert_to_character <- c("Attrition", "BusinessTravel","Department","Education","EducationField","EnvironmentSatisfaction","Gender","JobInvolvement","JobLevel","JobRole","JobSatisfaction","MaritalStatus","OverTime","PerformanceRating","RelationshipSatisfaction","StockOptionLevel","WorkLifeBalance")
hr_encoded=hr
hr_encoded[, convert_to_character] <- hr[, lapply(.SD, as.numeric), .SDcols = convert_to_character]

Handling the high correlation betweeen independent variable

hr_encoded <- one_hot(as.data.table(hr),dropUnusedLevels=TRUE)
hr_encoded<-hr_encoded[,-"Attrition_No"]

This graph shows the correlation with decreasing order.

hr_encoded<-as.data.frame(hr_encoded)
# remove features which are not used for correlation analysis
training <- select(hr_encoded, -c(Attrition_Yes))
# calculate correlation coefficient of each feature with survival
feature <- names(training)
corrSurvived <- data.frame(feature = feature, coef = rep(NA, length(feature)))
for (iFeature in 1:length(feature)){
    corrSurvived$coef[iFeature] <- cor(training[, iFeature],hr_encoded$Attrition_Yes)
}
# sort by correlation coefficient
corrSurvivedOrder <- corrSurvived[order(corrSurvived$coef, decreasing = FALSE), ]
ggplot(corrSurvivedOrder, aes(x = factor(feature, levels = feature), y = coef,fill=coef)) + 
    geom_bar(stat = "identity") + 
    coord_flip() + 
    xlab("Feature") + 
    ylab("Correlation Coefficient")

correlation plot

# function to calculate plot feature correlation matrix
getCorrMatrix <- function(featureList, showPlot = TRUE){
    # remove Survived from training set and order feature with respect to correlation coefficient to survived
    passengerCorr <- hr_encoded[, as.character(featureList)]
    # calculate correlation matrix
    corrMatrix <- cor(passengerCorr)
    # plot matrix
    if (showPlot) {corrplot(corrMatrix, method = "color", type = "upper")}
    corrMatrix
}
corrMatrix <- getCorrMatrix(rev(corrSurvivedOrder$feature))

# function to get data frame with pairwise correlation of features
getPairCorrelation <- function(corrMatrix){
    featureName <- colnames(corrMatrix)
    nFeature <- length(featureName)
    
    # set lower triangle of matrix to NA (these values are all redundant)
    corrMatrix[lower.tri(corrMatrix, diag = TRUE)] <- NA
    
    # convert matrix to data frame
    featurePair <- data.frame(feature1 = rep(featureName, nFeature), feature2 = rep(featureName, each = nFeature), coef = as.vector(corrMatrix))
    # remove NAs
    featurePair <- featurePair[!is.na(featurePair$coef), ]
    # calculate absolute value of correlation coefficient
    featurePair$coefAbs <- abs(featurePair$coef)
    # order by coefficient
    featurePair <- featurePair[order(featurePair$coefAbs, decreasing = TRUE), ]
    
    featurePair
}    
featureCorr <- getPairCorrelation(corrMatrix) 

Lets have a look at the ten feature pairs with the highes correlations:

kable(featureCorr[1:10, ]) %>% kable_styling(full_width = FALSE)
feature1 feature2 coef coefAbs
1084 Gender_Male Gender_Female -1.0000000 1.0000000
2551 OverTime_Yes OverTime_No -1.0000000 1.0000000
2495 MonthlyIncome JobLevel 0.9502999 0.9502999
1791 Department_Sales Department_Research & Development -0.9068183 0.9068183
726 JobRole_Human Resources Department_Human Resources 0.9049828 0.9049828
669 Department_Sales JobRole_Sales Executive 0.8088693 0.8088693
2548 JobLevel TotalWorkingYears 0.7822078 0.7822078
986 PerformanceRating PercentSalaryHike 0.7735500 0.7735500
2546 MonthlyIncome TotalWorkingYears 0.7728932 0.7728932
2287 YearsAtCompany YearsWithCurrManager 0.7692124 0.7692124
# plot histogram of correlation factors
ggplot(featureCorr, aes(coef)) + geom_histogram(binwidth = 0.1) + xlab("Correlation Coefficient")

4 Feature Reduction

1)Greedy Elimination

featureGE
 [1] "OverTime_No"                       "MaritalStatus_Single"              "EducationField_Technical Degree"   "JobRole_Manufacturing Director"   
 [5] "JobSatisfaction"                   "WorkLifeBalance"                   "DistanceFromHome"                  "BusinessTravel_Travel_Frequently" 
 [9] "MonthlyRate"                       "PercentSalaryHike"                 "JobInvolvement"                    "RelationshipSatisfaction"         
[13] "Gender_Female"                     "EnvironmentSatisfaction"           "HourlyRate"                        "EducationField_Human Resources"   
[17] "DailyRate"                         "EducationField_Other"              "TrainingTimesLastYear"             "TotalWorkingYears"                
[21] "JobRole_Healthcare Representative" "Education"                         "BusinessTravel_Non-Travel"         "JobRole_Sales Representative"     
[25] "EducationField_Medical"            "JobRole_Laboratory Technician"     "NumCompaniesWorked"                "JobRole_Research Director"        
[29] "Department_Research & Development" "JobRole_Research Scientist"        "MaritalStatus_Divorced"            "YearsInCurrentRole"               
[33] "JobRole_Manager"                   "EducationField_Marketing"          "YearsSinceLastPromotion"           "JobRole_Human Resources"          
[37] "EducationField_Life Sciences"      "MaritalStatus_Married"             "StockOptionLevel"                  "Age"                              
[41] "YearsWithCurrManager"              "JobRole_Sales Executive"           "BusinessTravel_Travel_Rarely"      "YearsAtCompany"                   
[45] "PerformanceRating"                 "JobLevel"                          "Department_Human Resources"        "Department_Sales"                 
[49] "MonthlyIncome"                     "OverTime_Yes"                      "Gender_Male"                      

PCA for feature reduction

pcaTraining
Importance of components:
                          PC1     PC2     PC3     PC4    PC5     PC6     PC7     PC8     PC9    PC10   PC11    PC12    PC13    PC14    PC15    PC16
Standard deviation     2.3239 1.87827 1.62404 1.49462 1.4513 1.42555 1.40214 1.34540 1.33444 1.26975 1.2323 1.15566 1.13587 1.11399 1.10214 1.08224
Proportion of Variance 0.1039 0.06784 0.05072 0.04296 0.0405 0.03908 0.03781 0.03481 0.03424 0.03101 0.0292 0.02568 0.02481 0.02386 0.02336 0.02252
Cumulative Proportion  0.1039 0.17170 0.22242 0.26538 0.3059 0.34497 0.38278 0.41759 0.45183 0.48284 0.5120 0.53772 0.56253 0.58640 0.60976 0.63228
                          PC17    PC18    PC19    PC20    PC21    PC22    PC23    PC24    PC25    PC26    PC27    PC28    PC29    PC30    PC31    PC32
Standard deviation     1.07025 1.06542 1.05691 1.03716 1.01844 1.00651 1.00119 0.98751 0.98057 0.97037 0.96322 0.95597 0.93864 0.93224 0.89044 0.83044
Proportion of Variance 0.02203 0.02183 0.02148 0.02069 0.01995 0.01948 0.01928 0.01875 0.01849 0.01811 0.01784 0.01757 0.01694 0.01671 0.01525 0.01326
Cumulative Proportion  0.65431 0.67614 0.69762 0.71831 0.73825 0.75773 0.77701 0.79576 0.81425 0.83236 0.85020 0.86778 0.88472 0.90144 0.91668 0.92995
                          PC33    PC34    PC35    PC36    PC37    PC38    PC39    PC40    PC41    PC42    PC43    PC44    PC45      PC46      PC47
Standard deviation     0.80075 0.72875 0.69784 0.69227 0.59715 0.52690 0.47494 0.46776 0.38485 0.33312 0.28375 0.22710 0.18605 2.873e-15 1.676e-15
Proportion of Variance 0.01233 0.01021 0.00937 0.00922 0.00686 0.00534 0.00434 0.00421 0.00285 0.00213 0.00155 0.00099 0.00067 0.000e+00 0.000e+00
Cumulative Proportion  0.94228 0.95249 0.96185 0.97107 0.97793 0.98327 0.98760 0.99181 0.99466 0.99679 0.99834 0.99933 1.00000 1.000e+00 1.000e+00
                            PC48      PC49      PC50      PC51      PC52
Standard deviation     1.202e-15 1.091e-15 8.913e-16 6.633e-16 3.165e-16
Proportion of Variance 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00
Cumulative Proportion  1.000e+00 1.000e+00 1.000e+00 1.000e+00 1.000e+00
y<-ggplot(hr[TotalWorkingYears > 0 & TotalWorkingYears < 10,.N,by=list(Attrition,OverTime)],aes(x=OverTime,y=N,fill=Attrition))+geom_bar(stat = "identity",width = 0.2,position = "dodge")+geom_text(aes(label=N), vjust=-0.30)+labs(x ="overtime", y = "Number of Employee")+ylim(0,400)
ggplotly(y)

The conclusion ,the most of the employees who have worked 0-5 years , their salary is less than 5000$,and are doing overtime and the job level 1 have higher probabilty of leaving the company.

JobRole ,Age, EnvironmentSatisfaction ,YearsAtCompany

x<-ggplot(hr,aes(YearsAtCompany,MonthlyIncome,color=EnvironmentSatisfaction,shape=Age))+geom_point()+facet_wrap(~Attrition)+labs(x = "Years At the Company",y="Monthly_Income")
ggplotly(x)

As we can see above that the employee of age group 20-30,number of years in this company is less than 5 and monthly salary is belowe $5000 are leaving the company the most.

Now we are knowe that at job level 1 the most of the employee ’s are leaving. so now i am going to check in job level 1 in which role the employees are leaving the most.

plotting the bar plot for the data representation.

ggplot(hr[JobLevel==1 & MonthlyIncome<5000 &TotalWorkingYears<=5 &YearsAtCompany<5 & Age=="20-30",.N,by=list(JobRole,Attrition,Department)],aes(x=JobRole,y=N,fill=Department))+geom_bar(stat = "identity",width = 0.2)+labs(x ="jobRole", y = "Number of employees")+facet_wrap(~Attrition)+coord_flip()+geom_text(aes(label=N), vjust=-0.2)

from this we can conclude that in job level 1,Age group is 20-30 ,monthly income is less than 5000$ ,totalworking years are less than 5 years,and years at company less than equal to 5 In job level one There are three department 1)Human Resource 2)Research and development 3)sales.

In job level one we have 4 job roles in a Department :- 1)Laboratory Technician 2)Research Scientist 3)Sales Representative 4)Human Resources

where the most of the employee ’s are leaving in job role of Laboratory Technician of Research and development department and Sales Representative Sales department.

MaritalStatus,WorkLifeBalance

x<-ggplot(hr,aes(WorkLifeBalance,MonthlyIncome ,color=MaritalStatus))+geom_point()+facet_wrap(~Attrition)+labs(x = "WorkLifeBalance",y="Monthly_Income")
ggplotly(x)

By this graph we can conclude that the employees who ’s monthy income is less than 10000$ ,Marital status is single and worklife balance are Better are leaving the company.

LS0tDQp0aXRsZTogIlRoZSBIUiBhbmFseXRpY3MgRURBIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KbGlicmFyaWVzDQpgYGB7cn0NCiNncmFwaCBwbG90dGluZyBwYWNrYWdlcy4NCmxpYnJhcnkoZGF0YS50YWJsZSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShnZ2FuaW1hdGUpDQpsaWJyYXJ5KGQzVHJlZSkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KG1sdG9vbHMpDQpsaWJyYXJ5KGNvcnJwbG90KQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KYGBgDQoNClJlYWRpbmcgdGhlIGRhdGEgaW50byBSIGFuZCBkcm9wcGluZyBPdmVyMTgsRW1wbG95ZWVDb3VudCxFbXBsb3llZU51bWJlcixTdGFuZGFyZEhvdXJzIGJlY2F1c2UgaW4gdGhlc2UgY29sdW1ucyB0aGUgZGF0YSBpcyBlaXRoZXIgdW5pcXVlIG9yIHRoZSBkYXRhIGlzIHNhbWUgZm9yIGFsbCB0aGUgcm93IGluIHRoZSB0YWJsZS4NCmBgYHtyfQ0KaHI8LWZyZWFkKCJXQV9Gbi1Vc2VDXy1IUi1FbXBsb3llZS1BdHRyaXRpb24uY3N2IixuYS5zdHJpbmdzID0gIk5BIixzdHJpbmdzQXNGYWN0b3JzID0gVFJVRSxkcm9wID0gYygiT3ZlcjE4IiwiRW1wbG95ZWVDb3VudCIsIkVtcGxveWVlTnVtYmVyIiwiU3RhbmRhcmRIb3VycyIpKQ0KYGBgDQoNCk5vdyBzZWluZyB0aGUgc3RydWN0dXJlIG9mIHRoZSBkYXRhIHRvIGdldCBzb21lIGlkZWEgb2YgdHlwZSBvZiBkYXRhIHByZXNlbnQgaW4gdGhlIGRhdGEgc2V0Lg0KYGBge3J9DQpzdHIoaHIpDQpgYGANCk5vdyB2aWV3aW5nICB0aGUgc3VtbWFyeSBvZiBhbGwgbnVtZXJpYyBjb2x1bW4gb2YgdGhlIGRhdGEgc2V0Lg0KYGBge3J9DQpzdW1tYXJ5KGhyKQ0KYGBgDQoNCmBgYHtyfQ0KY29sbmFtZXMoaHIpDQpgYGANCk5vdyB3ZSBhcmUgZ3JvdXBpbmcgdGhlIGFnZXMgYmFzZXMgYmFzZWQgb24gYWdlIGdyb3Vwcy4NCjIwLWJlbG93ZQ0KMjAtMzANCjMwLTQwDQo0MC01MA0KNTAtNjANCnNvIG5vdyB3ZSBhcmUgZGl2aWRpbmcgb3VyIGFuYWx5c2lzIG9uIGRpZmZlcmVudCBhZ2UgZ3JvdXAuDQpgYGB7cn0NCmhyWyxBZ2U6PWFzLmZhY3RvciggaWZlbHNlKA0KICBBZ2UgPD0gMjAgLCAnMTUtMjAnLA0KICAgIGlmZWxzZShBZ2UgPjIwICAmIEFnZSA8PSAzMCwnMjAtMzAnICwNCiAgICAgIGlmZWxzZShBZ2UgPiAzMCAgJiBBZ2UgPD0gNDAsICczMC00MCcsIA0KICAgICAgICAgIGlmZWxzZShBZ2UgPjQwICYgQWdlIDw9NTAsJzQwLTUwJywnNTAtNjAnKSAgICkNCiAgICApDQogICkNCikNCl0NCmBgYA0KDQoNCmBgYHtyfQ0KY29sbmFtZXMoaHIpDQpgYGANCg0KDQpJbiBvdXIgZGF0YSB3ZSBoYXZlIHNvbWUgZmVhdHVyZXMgIkVkdWNhdGlvbiIsICJFbnZpcm9ubWVudFNhdGlzZmFjdGlvbiIsIkpvYkludm9sdmVtZW50IiwiSm9iTGV2ZWwiLCJKb2JTYXRpc2ZhY3Rpb24iLCJQZXJmb3JtYW5jZVJhdGluZyIsIlJlbGF0aW9uc2hpcFNhdGlzZmFjdGlvbiIsIlN0b2NrT3B0aW9uTGV2ZWwiLCIgV29ya0xpZmVCYWxhbmNlIiB3aGljaCBhcmUgbnVtZXJpYyBidXQgdGhlIHNlbnNlIHRoZXkgbWFrZSBpcyBmYWN0b3JzLHNvIG5vdyBjb252ZXJ0aW5nIHRob3NlIHZhcmlhYmxlIHRvIGZhY3RvcnMuDQpgYGB7cn0NCmZhY3RfY29sdW1uczwtYygiRWR1Y2F0aW9uIiwgIkVudmlyb25tZW50U2F0aXNmYWN0aW9uIiwiSm9iSW52b2x2ZW1lbnQiLCJKb2JMZXZlbCIsIkpvYlNhdGlzZmFjdGlvbiIsIlBlcmZvcm1hbmNlUmF0aW5nIiwiUmVsYXRpb25zaGlwU2F0aXNmYWN0aW9uIiwiU3RvY2tPcHRpb25MZXZlbCIsIldvcmtMaWZlQmFsYW5jZSIpDQpocjwtYXMuZGF0YS5mcmFtZShocikNCmhyW2ZhY3RfY29sdW1uc108LWxhcHBseShocltmYWN0X2NvbHVtbnNdLCBhcy5mYWN0b3IpDQpocjwtYXMuZGF0YS50YWJsZShocikNCmBgYA0KDQpBbmFseXNpcyBvbiBhZ2UgZ3JvdXAuDQoNCk5vdyB3ZSBhcmUgY29tcGFyaW5nIHdoaWNoIGFnZSBncm91cHMgYXJlIHRyYXZlbGxpbmcgdGhlIG1vc3QuDQpgYGB7cn0NCmdncGxvdChoclssLk4sYnk9bGlzdChBZ2UsQnVzaW5lc3NUcmF2ZWwsR2VuZGVyKV0sYWVzKHg9QWdlLHk9TixmaWxsPUdlbmRlcikpK2dlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLHdpZHRoID0gMC42LHBvc2l0aW9uID0gImRvZGdlIikrZmFjZXRfZ3JpZCh+QnVzaW5lc3NUcmF2ZWwpK2dlb21fdGV4dChhZXMobGFiZWw9TiksIHZqdXN0PS0wLjMpK2xhYnMoeCA9IlRoZSBBZ2UgR3JvdXBzIiwgeSA9ICJOdW1iZXIgb2YgRW1wbG95ZWUiKQ0KYGBgDQoNCg0KYGBge3J9DQpzYWxhcnlfbW9udGhseV9kZXA8LWZ1bmN0aW9uKGFnZSx0eXBlVHJhdmVsKXsNCng8LWdncGxvdChocltBZ2U9PWFnZSYgQnVzaW5lc3NUcmF2ZWw9PXR5cGVUcmF2ZWwsLk4sYnk9bGlzdChKb2JSb2xlLEpvYlNhdGlzZmFjdGlvbixNb250aGx5SW5jb21lLCBEZXBhcnRtZW50KV0sYWVzKHg9Sm9iUm9sZSx5PU1vbnRobHlJbmNvbWUsZmlsbD1Kb2JSb2xlKSkrZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLHdpZHRoID0gMC4zKSArZmFjZXRfd3JhcCh+RGVwYXJ0bWVudCxuY29sID0gMSxzY2FsZXMgPSAiZnJlZSIpDQpnZ3Bsb3RseSh4KQ0KfQ0KYGBgDQoNCmBgYHtyfQ0Kc2FsYXJ5X21vbnRobHlfZGVwKCIzMC00MCIsIlRyYXZlbF9SYXJlbHkiKQ0KYGBgDQoNCmBgYHtyfQ0KI2ZvciBtb3ZlbWVudA0KI3RyYW5zaXRpb25fc3RhdGVzKEVudmlyb25tZW50U2F0aXNmYWN0aW9uLCAuMDEsIC4wMDEpK2Vhc2VfYWVzKCdjdWJpYy1pbi1vdXQnKQ0KI2ZvciBwbG90dGluZyBtdWx0aXBsb3Qgb24gb25lIHBhZ2UgDQojbXVsdGlwbG90KHAxLCBwMiwgcDMsIHA0LCBjb2xzPTIpDQpgYGANCk5vdyBmaW5kaW5nIHRoZSBpbnNpZ2h0cyByZWdhcmRpbmcgZW1wbG95ZWUgdHVybiBvdmVyLg0Kc28gd2UgZm91bmQgZmV3IGZlYXR1cmUgd2hpY2ggYXJlIHZlcnkgaW1wb3J0YW50IGZvciBlbXBsb3llZSB0dXJub3Zlci4NCk1vbnRobHlJbmNvbWUgLFRvdGFsV29ya2luZ1llYXJzLE92ZXJUaW1lICxKb2JMZXZlbCANCkpvYlJvbGUgLEFnZSwgRW52aXJvbm1lbnRTYXRpc2ZhY3Rpb24gLFllYXJzQXRDb21wYW55DQoNCg0KDQoNCg0KDQoNCg0Kd2hhdCBwZXJjZW50YWdlIG9mIGVtcGxveWVlcyBsZWF2aW5nIHRoZSBjb21wYW55Lg0KYGBge3J9DQojIENyZWF0ZSB0ZXN0IGRhdGEuDQpkYXQgPSBkYXRhLmZyYW1lKGNvdW50PWMoMTAsIDYwLCAzMCksIGNhdGVnb3J5PWMoIkEiLCAiQiIsICJDIikpDQogDQojIEFkZCBhZGRpdGlvbiBjb2x1bW5zLCBuZWVkZWQgZm9yIGRyYXdpbmcgd2l0aCBnZW9tX3JlY3QuDQpkYXQkZnJhY3Rpb24gPSBkYXQkY291bnQgLyBzdW0oZGF0JGNvdW50KQ0KZGF0ID0gZGF0W29yZGVyKGRhdCRmcmFjdGlvbiksIF0NCmRhdCR5bWF4ID0gY3Vtc3VtKGRhdCRmcmFjdGlvbikNCmRhdCR5bWluID0gYygwLCBoZWFkKGRhdCR5bWF4LCBuPS0xKSkNCiANCiMgTWFrZSB0aGUgcGxvdA0KcDEgPSBnZ3Bsb3QoZGF0LCBhZXMoZmlsbD1jYXRlZ29yeSwgeW1heD15bWF4LCB5bWluPXltaW4sIHhtYXg9NCwgeG1pbj0zKSkgKw0KICAgICBnZW9tX3JlY3QoKSArDQogICAgIGNvb3JkX3BvbGFyKHRoZXRhPSJ5IikgKw0KICAgICB4bGltKGMoMCwgNCkpICsNCiAgICAgdGhlbWUocGFuZWwuZ3JpZD1lbGVtZW50X2JsYW5rKCkpICsNCiAgICAgdGhlbWUoYXhpcy50ZXh0PWVsZW1lbnRfYmxhbmsoKSkgKw0KICAgICB0aGVtZShheGlzLnRpY2tzPWVsZW1lbnRfYmxhbmsoKSkgKw0KICAgICBhbm5vdGF0ZSgidGV4dCIsIHggPSAwLCB5ID0gMCwgbGFiZWwgPSAiTXkgUmluZyBwbG90ICEiKSArDQogICAgIGxhYnModGl0bGU9IiIpDQoNCmBgYA0KTm93IHBsb3RpbmcgdGhlIHNjYXR0ZXIgcGxvdCBmb3IgdG8gc2VlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgbW9udGhseSBpbmNvbWUgLFRvdGFsIHdvcmtpbmcgeWVhcnMgYW5kIHRoZSBhdHRyaXRpb24gcmF0ZS4NCmBgYHtyfQ0KeDwtZ2dwbG90KGhyLGFlcyhUb3RhbFdvcmtpbmdZZWFycyxNb250aGx5SW5jb21lLGNvbG9yPUpvYkxldmVsLHNoYXBlPU92ZXJUaW1lKSkrZ2VvbV9wb2ludCgpK2ZhY2V0X3dyYXAofkF0dHJpdGlvbikrbGFicyh4ID0gIlRvdGFsIFdvcmtpbmcgWWVhcnMiLHk9Ik1vbnRobHlfSW5jb21lIikNCmdncGxvdGx5KHgpDQpgYGANCkFzIHdlIHNlZSBpbiB0aGUgYWJvdmUgZ3JhcGggdGhhdCB0aGUgZW1wbG95ZWVzIHdobyAncyBtb250aGx5IGluY29tZSBpcyBsZXNzIHRoYW4gMTAwMDAkICx0b3RhbCB3b3JraW5nIHllYXJzIGluIHJhbmdlIDAtMjAgLGpvYiBsZXZlbCAxLTMgYXJlIGhhdmluZyB0aGUgbWF4aW11bSBhdHRyaXRpb24uDQoNCnNvIG5vdyBpIGFtIGRpdmlkaW5nIG15IGFuYWx5c2lzIGludG8gMiBwYXJ0cyBhcyBpbiAgbW9udGhseV9pbmNvbWUgbGVzcyB0aGFuIDUwMDAgYW5kIDUwMDAtMTAwMDAgZGlmZmVyZW50IGFzIGRpZmZlcmVudCBqb2IgbGV2ZWwgcGVvcGxlIGFyZSA6LQ0KMSlhbmFseXNpcyBvbiBlbXBsb3llZSB3aG8gJ3MgbW9udGhseSBpbmNvbWUgaXMgbGVzcyB0aGFuIDUwMDAkDQoyKWFuYWx5c2lzIG9uIGVtcGxveWVlIHdobyAncyBtb250aGx5IGluY29tZSBpcyBpbiBiZXR3ZWVuIDUwMDAkIC0gMTAwMDAkDQoNCg0KDQpOb3cgZG9pbmcgYW5hbHlzaXMgb24gdGhlIGVtcGxveWVlcyB3aG8gJ3Mgc2FsYXJ5IGlzIGxlc3MgdGhhbiA1MDAwJA0KTm93IHdlIGZ1cnRoZXIgZGl2aWRpbmcgb3VyIGFuYWx5c2lzIGluIHRvIHR3byBwYXJ0cyB0b3RhbCB3b3JraW5nIHllYXJzIA0KMSkgTGVzcyB0aGFuIDUNCjIpIEJldHdlZW4gNSB0byAxMCB5ZXJhcw0KDQpOb3cgSSBhbSBnb2luZyB0byB2aWV3ICBqb2IgbGV2ZWwgb2YgZW1wbG95ZWUgYW5kIHdoZWF0aGVyIHRoZSBlbXBsb3llZSBpcyB3b3JraW5nIG92ZXIgdGltZSAsb2YgdGhvc2UgZW1wbG95ZWUgJ3Mgd2hvICdzIG1vbnRobHkgaW5jb21lIGlzIGxlc3MgdGhhbiA1MDAwJCBhbmQgdG90YWwgd29raW5nIHllYXJzIGlzIGluIHRoZSByYW5nZSAwLTUgeWVhcnMuDQpgYGB7cn0NCng8LWdncGxvdChocltNb250aGx5SW5jb21lPD01MDAwICYgVG90YWxXb3JraW5nWWVhcnMgPD0gNSAmIEF0dHJpdGlvbj09IlllcyIsLk4sYnk9bGlzdChKb2JMZXZlbCxPdmVyVGltZSxNb250aGx5SW5jb21lLFRvdGFsV29ya2luZ1llYXJzKV0sYWVzKFRvdGFsV29ya2luZ1llYXJzLE1vbnRobHlJbmNvbWUsY29sb3I9Sm9iTGV2ZWwpKStnZW9tX3BvaW50KCkrZmFjZXRfd3JhcCh+T3ZlclRpbWUpK2xhYnMoeCA9ICJUb3RhbCBXb3JraW5nIFllYXJzIix5PSJNb250aGx5X0luY29tZSIpK2xhYnModGl0bGUgPSAiT3ZlciBUaW1lIixmaWxsPSJBZ2UgR3JvdXAiKQ0KZ2dwbG90bHkoeCkNCmBgYA0KYGBge3J9DQoNCmBgYA0KDQpzbyBieSBzZWluZyB0aGUgYWJvdmUgZ3JhcGggd2UgY2FuIGluZmVyIHRoYXQgam9iIGxldmVsIDEgYW5kIDIgZW1wbG95ZWUgJ3Mgd2hvICdzIHNhbGFyeSBpcyBsZXNzIHRoYW4gIDUwMDAkIGFuZCB0aGUgdG90YWwgd29ya2luZyB5ZWFycyBpcyBsZXNzIHRoYW4gNSBoYXZlIGhpZ2ggcHJvYmFiaWxpdHkgb2YgIGxlYXZpbmcgdGhlIGNvbXBhbnksdGhlaXIgaXMgbm8gbXVjaCBkaWZmZXJlbmNlIG9mIGVtcGxveWVlIHR1cm5vdmVyIGluIHRoZXNlIGVtcGxveWVlcw0KDQpCdXQgaWYgd2Ugc2VlIGluIG1vcmUgZGV0YWlsIDEgeWVhciBleHBlcmllbmNlIGFuZCBqb2IgbGV2ZWwgMSBlbXBsb3llZSBhcmUgbGVhdmluZyB0aGUgbW9zdC4NCg0KTm93IHdlIGFyZSBjaGVja2luZyB0aGVpciBFbnZpcm9ubWVudCBzYXRpc2ZhY3Rpb24gcmF0aW5nICxqb2Iscm9sZXMsQWdlIGdyb3VwLg0KYGBge3J9DQp4PC1nZ3Bsb3QoaHJbTW9udGhseUluY29tZSA8PSA1MDAwICYgVG90YWxXb3JraW5nWWVhcnMgPD0gMSAmIEpvYkxldmVsID09IDEgICZBdHRyaXRpb249PSJZZXMiLC5OLGJ5PWxpc3QoSm9iUm9sZSxFbnZpcm9ubWVudFNhdGlzZmFjdGlvbixBZ2UpXSxhZXMoeD1Kb2JSb2xlLHk9TixmaWxsPUFnZSkpK2dlb21fYmFyKHN0YXQ9ImlkZW50aXR5Iix3aWR0aCA9IDAuMykgK2ZhY2V0X3dyYXAofkVudmlyb25tZW50U2F0aXNmYWN0aW9uICxuY29sID0gMSxzY2FsZXMgPSAiZnJlZSIpK2Nvb3JkX2ZsaXAoKStsYWJzKHRpdGxlID0gIkVudmlyb25tZW50U2F0aXNmYWN0aW9uIFJhdGluZyAoMS00KSIseT0iVGhlIE51bWJlciBvZiBFbXBsb3llZXMgbW92aW5nIG91dCBvZiB0aGUgY29tcGFueS4gIixmaWxsPSJBZ2UgR3JvdXAiKQ0KZ2dwbG90bHkoeCkNCmBgYA0KQnkgdGhlIGFib3ZlIGdyYXBoIHdlIGNhbiBpbmZlciBieSBqb2Igcm9sZSB0aGF0IA0KDQojMSkgcmVzZWFyY2ggc2NpZW50aXN0IA0KDQpUaGVyZSBhcmUgdG90YWwgMTIgZW1wbG95ZWVzIHdoZXJlIA0KDQoxKSAzIGhhdmUgcmF0ZWQgRW52aXJvbm1lbnRTYXRpc2ZhY3Rpb24gIExvdyAsMy8xMiA9IDI1JQ0KMikgMSBoYXZlIHJhdGVkIEVudmlyb25tZW50U2F0aXNmYWN0aW9uICBIaWdoICwxLzEyID0gMC4wOCUNCjMpIDggaGF2ZSByYXRlZCBFbnZpcm9ubWVudFNhdGlzZmFjdGlvbiAgdmVyeSBIaWdoID0gNjYlDQoNCkF0IHRoZSBhZ2UgZ3JvdXAgb2YgMzAtNDAgdGhlIGVtcGxveWVlIHdhcyBub3Qgc2F0aXNmaWVkLkF0IHRoZSBhZ2UgZ3JvdXAgb2YgMTUtMjAgdGhlIGVtcGxveWVlIGFyZSB2ZXJ5IG11Y2ggc2F0aXNmaWVkIHdpdGggdGhlIEVudmlyb25tZW50IGJ1dCBzdGlsbCBsZWF2aW5nIHRoZSBjb21wYW55Lg0KDQoNCnNvIG5vdyB3ZSBhcmUgZ29pbmcgdG8gZmluZCBvdXQgd2h5IGF0IGFnZSBncm91cCBvZiAxNS0yMCAsd2hvICdzIGVudmlyb25tZW50IHNhdGlzZmFjdGlvbiBpcyBoaWdoIGFuZCB2ZXJ5IGhpZ2ggYXJlIHN0aWxsIGxlYXZpbmcgdGhlIGNvbXBhbnkuDQoNClRoZSBtYXhpbXVtIG1vbnRobHkgaW5jb21lIG9mIG9uZSB5ZWFyIGV4cGVyaWVuY2UgcmVzZWFyY2ggU2NpZW50aXN0IGJ5IHRoZSBoaXN0b3JpY2FsIGRhdGEgb2YgdGhlIGNvbXBhbnkgIGlzIDogMjk5NA0KVGhlIG1pbmltdW0gbW9udGhseSBpbmNvbWUgb2Ygb25lIHllYXIgZXhwZXJpZW5jZSByZXNlYXJjaCBzY2llbnRpc3QgYnkgdGhlIGhpc3RvcmljYWwgZGF0YSBvZiB0aGUgY29tcGFueSAgaXMgOiAxMDA5DQoNClRoZSBtYXhpbXVtIG1vbnRobHkgaW5jb21lIG9mICByZXNlYXJjaCBTY2llbnRpc3QgYnkgdGhlIGhpc3RvcmljYWwgZGF0YSBvZiB0aGUgY29tcGFueSAgaXMgOjk3MjQNClRoZSBtaW5pbXVtIG1vbnRobHkgaW5jb21lIG9mICByZXNlYXJjaCBzY2llbnRpc3QgYnkgdGhlIGhpc3RvcmljYWwgZGF0YSBvZiB0aGUgY29tcGFueSAgaXMgOjEwMDkNCmBgYHtyfQ0KDQpgYGANCg0KDQpgYGB7cn0NCng8LWdncGxvdChocltNb250aGx5SW5jb21lPD01MDAwICYgVG90YWxXb3JraW5nWWVhcnMgPT0gMSAmQXR0cml0aW9uPT0iWWVzIiYgKEVudmlyb25tZW50U2F0aXNmYWN0aW9uPT0zIHwgRW52aXJvbm1lbnRTYXRpc2ZhY3Rpb249PTQpJkpvYlJvbGU9PSJSZXNlYXJjaCBTY2llbnRpc3QiLC5OLGJ5PWxpc3QoT3ZlclRpbWUsTW9udGhseUluY29tZSxZZWFyc0F0Q29tcGFueSxXb3JrTGlmZUJhbGFuY2UpXSxhZXMoWWVhcnNBdENvbXBhbnksTW9udGhseUluY29tZSxmaWxsPSBXb3JrTGlmZUJhbGFuY2UgKSkrZ2VvbV9wb2ludCgpK2ZhY2V0X3dyYXAofk92ZXJUaW1lKStsYWJzKHggPSAiIix5PSJNb250aGx5X0luY29tZSIpK2xhYnModGl0bGUgPSAiT3ZlciBUaW1lIix4PSJ5ZWFycyBhdCB0aGUgY29tcGFueSIsZmlsbD0iV29yayBMaWZlIEJhbGFuY2UiKQ0KZ2dwbG90bHkoeCkNCmBgYA0KVGhlIG1vc3Qgb2YgdGhlIGVtcGxveWVlIHdobyBjYW1lIHRvIHRoaXMgcG9zdCBpbiB0aGlzIGNvbXBhbnkgYXJlIGZyZXNoZXJzIGFuZCB0aGV5IGdvdCAxIHllYXIgb2YgZXhwZXJpZW5jZSBpbiB0aGUgY3VycmVudCBjb21wYW55Lg0KVGhlIHJlc2VhcmNoIHNjaWVudGlzdCB3aG8gYXJlIGRvaW5nIG92ZXIgdGltZSBhbmQgZ2V0dGluZyB0aGUgc2ltaWxhciBtb250aGx5IGluY29tZSBhcyB0aGUgZW1wbG95ZWUgd2hvIGFyZSBub3QgZG9pbmcgb3ZlciB0aW1lIGFyZSBsZWF2aW5nIHRoZSBjb21wYW55Lg0KdGhlaXIgd29yayBsaWZlIGJhbGFuY2UgaXMgZ29vZC4NCg0KDQpgYGB7cn0NCng8LWdncGxvdChocltNb250aGx5SW5jb21lPD01MDAwICYgVG90YWxXb3JraW5nWWVhcnMgPT0gMSAmQXR0cml0aW9uPT0iWWVzIiYgKEVudmlyb25tZW50U2F0aXNmYWN0aW9uPT0zIHwgRW52aXJvbm1lbnRTYXRpc2ZhY3Rpb249PTQpJkpvYlJvbGU9PSJSZXNlYXJjaCBTY2llbnRpc3QiJk92ZXJUaW1lPT0iWWVzIiwuTixieT1saXN0KEJ1c2luZXNzVHJhdmVsLFN0b2NrT3B0aW9uTGV2ZWwgLERpc3RhbmNlRnJvbUhvbWUgLFllYXJzV2l0aEN1cnJNYW5hZ2VyLCAgSm9iSW52b2x2ZW1lbnQgLFBlcmNlbnRTYWxhcnlIaWtlLFJlbGF0aW9uc2hpcFNhdGlzZmFjdGlvbiwgRWR1Y2F0aW9uRmllbGQpXSxhZXMoUGVyY2VudFNhbGFyeUhpa2UsUmVsYXRpb25zaGlwU2F0aXNmYWN0aW9uICxmaWxsPSAgSm9iSW52b2x2ZW1lbnQgICkpK2dlb21fcG9pbnQoKStmYWNldF93cmFwKH5FZHVjYXRpb25GaWVsZCkrbGFicyh4ID0gIlBlcmNlbnRTYWxhcnlIaWtlIix5PSJSZWxhdGlvbnNoaXBTYXRpc2ZhY3Rpb24gIikrbGFicyh0aXRsZSA9ICJFZHVjYXRpb25GaWVsZCIsZmlsbD0iSm9iSW52b2x2ZW1lbnQiKQ0KZ2dwbG90bHkoeCkNCg0KYGBgDQpUaGUgZW1wbG95ZWVzIHdobyBhcmUgZnJlcXVlbnRseSB0cmF2ZWxsaW5nIHRoZSBidXNzaW5lc3MgdHJpcHMgYXJlIGdlbmVyYWxseSBtb3Zpbmcgb3V0IG9mIHRoZSBjb21wYW55IGFuZCB0aGVpciBqb2IgaW52b2xtZW50IGlzIGFsc28gbWVkaXVtLlRoZWlyIHJlbGF0aW9uc2hpcHNhdGlzZmFjdGlvbiByYXRpbmcgYWxzbyBpcyB2ZXJ5IGxvdyANCg0KVGhlIG1pbmltdW0gcGVyY2VudGFnZSBoaWtlcyBnaXZlbiB0byByZXNlYXJjaCBzY2llbnRpc3QgaXMgOiAxMSUNClRoZSBtYXhpbXVtIHBlcmNlbnRhZ2UgaGlrZXMgZ2l2ZW4gdG8gcmVzZWFyY2ggc2NpZW50aXN0IGlzIDogMjUlDQoNCnNvIHRoZSBwZXJjZW50YWdlIGhpa2VzIGFyZSBhbHNvIGdpdmVuIGdvb2QgdG8gdGhlc2UgZW1wbG95ZWVzLg0KDQpgYGB7cn0NCmhyWyx7DQogICBUb3RhbCA9IC5OIA0KICAuU0RbLGxpc3QoUGVyPSguTi9Ub3RhbCkqMTAwLGJ5PUF0dHJpdGlvbildDQp9XQ0KYGBgDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KYGBge3J9DQp4PC1nZ3Bsb3QoaHJbTW9udGhseUluY29tZSA8PSA1MDAwICYgVG90YWxXb3JraW5nWWVhcnMgPD0gNSAmIChKb2JMZXZlbCA9PSAyIHwgIEpvYkxldmVsID09IDEpICAmQXR0cml0aW9uPT0iWWVzIiwuTixieT1saXN0KEpvYlJvbGUsRGVwYXJ0bWVudCxBdHRyaXRpb24sRW52aXJvbm1lbnRTYXRpc2ZhY3Rpb24sQWdlKV0sYWVzKHg9Sm9iUm9sZSx5PU4sZmlsbD1BZ2UpKStnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsd2lkdGggPSAwLjMpICtmYWNldF93cmFwKH5FbnZpcm9ubWVudFNhdGlzZmFjdGlvbiAsbmNvbCA9IDEsc2NhbGVzID0gImZyZWUiKStjb29yZF9mbGlwKCkrbGFicyh0aXRsZSA9ICJFbnZpcm9ubWVudFNhdGlzZmFjdGlvbiBSYXRpbmcgKDEtNCkiLHk9IlRoZSBOdW1iZXIgb2YgRW1wbG95ZWVzIG1vdmluZyBvdXQgb2YgdGhlIGNvbXBhbnkuICIsZmlsbD0iQWdlIEdyb3VwIikNCmdncGxvdGx5KHgpDQpgYGANCkFzIHdlIGNhbiBzZWUgaW4gdGhlIGFib3ZlIGdyYXBoIHdlIGNhbiBpbmZlciB0aGF0IA0KRm9yIHRoZSBlbXBsb3llZSAncyB3aG8gJ3MgbW9udGhseSBpbmNvbWUgaXMgYmVsb3cgNTAwMCQgLHRvdGFsIHdvcmtpbmcgeWVhcnMgaXMgbGVzcyB0aGFuIDUgLHdlIGhhdmUgb25seSB0d28gam9iIGxldmVscyANCg0KRm9yIGpvYiBsZXZlbCAxIHRoZXNlIGFyZSB0aGUgam9iIHJvbGVzIGFuZCBkZXBhcnRtZW50IDoNCjE6TGFib3JhdG9yeSBUZWNobmljaWFuIChSZXNlYXJjaCAmIERldmVsb3BtZW50IGRlcGFydG1lbnQpDQoyOlJlc2VhcmNoIFNjaWVudGlzdCAoUmVzZWFyY2ggJiBEZXZlbG9wbWVudCBkZXBhcnRtZW50KQ0KMzpTYWxlcyBSZXByZXNlbnRhdGl2ZSAgKFNhbGVzIGRlcGFydG1lbnQpDQo0Okh1bWFuIFJlc291cmNlcyAgKEh1bWFuIFJlc291cmNlcyBkZXBhcnRtZW50KQ0KDQpGb3Igam9iIGxldmVsIDIgdGhlc2UgYXJlIHRoZSBqb2Igcm9sZXMgYW5kIGRlcGFydG1lbnQgOg0KMTpNYW51ZmFjdHVyaW5nIERpcmVjdG9yIChSZXNlYXJjaCAmIERldmVsb3BtZW50IGRlcGFydG1lbnQpDQoyOlNhbGVzIEV4ZWN1dGl2ZSAoU2FsZXMgZGVwYXJ0bWVudCkNCjM6SGVhbHRoY2FyZSBSZXByZXNlbnRhdGl2ZSAgKFJlc2VhcmNoICYgRGV2ZWxvcG1lbnQgZGVwYXJ0bWVudCkNCjQ6U2FsZXMgUmVwcmVzZW50YXRpdmUgIChTYWxlcyBkZXBhcnRtZW50KQ0KNTpMYWJvcmF0b3J5IFRlY2huaWNpYW4gKFJlc2VhcmNoICYgRGV2ZWxvcG1lbnQgZGVwYXJ0bWVudCkNCg0KQnkgc2VpbmcgdGhlIEVudmlyb25tZW50U2F0aXNmYWN0aW9uICByYXRpbmcgb2YgdGhlIGVtcGxveWVlIHdobyAgYXJlIGxlYXZpbmcgdGhlIGNvbXBhbnkgd2UgY2FuIGluZmVyIHRoYXQgOg0KDQpBdCBMYWJvcmF0b3J5IFRlY2huaWNpYW4gam9iIHJvbGUgd2UgaGF2ZSB2ZXJ5IGhpZ2ggbnVtYmVyIG9mIGVtcGxveWVlcyB0dXJub3Zlcix0aGVpciBFbnZpcm9ubWVudCBzYXRpc2ZhY3Rpb24gcmF0aW5nIGlzIGxvdyBpbiBhbGwgdGhlIGFnZSBncm91cCAncyBvZiB0aGUgZW1wbG95ZWUuICANCg0KDQoNCg0KDQoNCldlIGNhbiBzZWUgZnJvbSB0aGUgYWJvdmUgZ3JhcGggaW4gam9iIHJvbGUgb2YgU2FsZXMgUmVwcmVzZW50YXRpdmUoU2FsZXMgZGVwYXJ0bWVudCksUmVzZWFyY2ggU2NpZW50aXN0IChSZXNlYXJjaCAmIERldmVsb3BtZW50IGRlcGFydG1lbnQpLExhYm9yYXRvcnkgVGVjaG5pY2lhbiAoUmVzZWFyY2ggJiBEZXZlbG9wbWVudCBkZXBhcnRtZW50KSB0aGVzZSBlbXBsb3llZSBhcmUgbGVhdmluZyB0aGUgY29tcGFueSBpbiBhIGxhcmdlciBzY2FsZSBhcyBjb21wYXJlIHRvIHRoZSBvdGhlciBqb2Igcm9sZXMuLg0KDQogQWdlICAgICAgICAgICBZZWFyc0F0Q29tcGFueSAgRW52aXJvbm1lbnRTYXRpc2ZhY3Rpb24gICAgICAgICAgICBNYXJpdGFsU3RhdHVzIA0KDQpOb3cgSSBhbSBnb2luZyB0byBsb29rIGFnZSx5ZWFyc0F0Y29tcGFueSxFbnZpcm9ubWVudHNhdGlzZmFjdGlvbixNYXJpdGFsU3RhdHVzLm9mIHRoZSBlbXBsb3llZXMgd2hvICdzIGpvYiByb2xlIGFyZSAgU2FsZXMgUmVwcmVzZW50YXRpdmUoU2FsZXMgZGVwYXJ0bWVudCksUmVzZWFyY2ggU2NpZW50aXN0IChSZXNlYXJjaCAmIERldmVsb3BtZW50IGRlcGFydG1lbnQpLExhYm9yYXRvcnkgVGVjaG5pY2lhbiAoUmVzZWFyY2ggJiBEZXZlbG9wbWVudCBkZXBhcnRtZW50KQ0KYGBge3IsZWNobz1GQUxTRX0NCmdncGxvdChocltNb250aGx5SW5jb21lIDw9IDUwMDAgJiBUb3RhbFdvcmtpbmdZZWFycyA8PSA1JiAoSm9iTGV2ZWwgPT0gMiB8ICBKb2JMZXZlbCA9PSAxKSAgJiBBdHRyaXRpb249PSJZZXMiLC5OLGJ5PWxpc3QoQWdlLFllYXJzQXRDb21wYW55LEVudmlyb25tZW50U2F0aXNmYWN0aW9uLE1hcml0YWxTdGF0dXMsV29ya0xpZmVCYWxhbmNlICldLCBhZXMoeD0iIiwgeT1OLCBncm91cD1NYXJpdGFsU3RhdHVzLCBjb2xvcj1NYXJpdGFsU3RhdHVzLCBmaWxsPU1hcml0YWxTdGF0dXMpKSArDQogIGdlb21fYmFyKHdpZHRoID0gMSwgc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgY29vcmRfcG9sYXIoInkiLCBzdGFydD0wKSArIGZhY2V0X3dyYXAofldvcmtMaWZlQmFsYW5jZSkgKw0KICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIHBhbmVsLmdyaWQgID0gZWxlbWVudF9ibGFuaygpKStsYWJzKHRpdGxlID0gIldvcmsgTGlmZSBCYWxhbmNlIFJhdGluZygxLTQpIix5PSJUaGUgTnVtYmVyIG9mIEVtcGxveWVlcyBtb3Zpbmcgb3V0IG9mIHRoZSBjb21wYW55IikNCmBgYA0KDQoNCmBgYHtyfQ0Kbm4gPC0gaHJbLGxpc3QoQWdlLFllYXJzQXRDb21wYW55LEVudmlyb25tZW50U2F0aXNmYWN0aW9uLE1hcml0YWxTdGF0dXMpXQ0KDQpkM3RyZWUobGlzdChyb290ID0gZGYydHJlZShyb290bmFtZSA9ICdPdmVyYWxsJyxzdHJ1Y3QgPSBhcy5kYXRhLmZyYW1lKG5uKSksDQogICBsYXlvdXQgPSAnY29sbGFwc2UnKSkNCmBgYA0KDQoNCg0KDQpgYGB7cn0NCnN0cihocikNCmBgYA0KDQoNCg0KYGBge3J9DQpzdHIoaHIpDQpgYGANCg0KDQoNCg0KYGBge3J9DQpjb252ZXJ0X3RvX2NoYXJhY3RlciA8LSBjKCJBdHRyaXRpb24iLCAiQnVzaW5lc3NUcmF2ZWwiLCJEZXBhcnRtZW50IiwiRWR1Y2F0aW9uIiwiRWR1Y2F0aW9uRmllbGQiLCJFbnZpcm9ubWVudFNhdGlzZmFjdGlvbiIsIkdlbmRlciIsIkpvYkludm9sdmVtZW50IiwiSm9iTGV2ZWwiLCJKb2JSb2xlIiwiSm9iU2F0aXNmYWN0aW9uIiwiTWFyaXRhbFN0YXR1cyIsIk92ZXJUaW1lIiwiUGVyZm9ybWFuY2VSYXRpbmciLCJSZWxhdGlvbnNoaXBTYXRpc2ZhY3Rpb24iLCJTdG9ja09wdGlvbkxldmVsIiwiV29ya0xpZmVCYWxhbmNlIikNCmhyX2VuY29kZWQ9aHINCmhyX2VuY29kZWRbLCBjb252ZXJ0X3RvX2NoYXJhY3Rlcl0gPC0gaHJbLCBsYXBwbHkoLlNELCBhcy5udW1lcmljKSwgLlNEY29scyA9IGNvbnZlcnRfdG9fY2hhcmFjdGVyXQ0KYGBgDQoNCg0KDQoNCkhhbmRsaW5nIHRoZSBoaWdoIGNvcnJlbGF0aW9uIGJldHdlZWVuIGluZGVwZW5kZW50IHZhcmlhYmxlDQpgYGB7cn0NCmhyX2VuY29kZWQgPC0gb25lX2hvdChhcy5kYXRhLnRhYmxlKGhyKSxkcm9wVW51c2VkTGV2ZWxzPVRSVUUpDQpocl9lbmNvZGVkPC1ocl9lbmNvZGVkWywtIkF0dHJpdGlvbl9ObyJdDQpgYGANCg0KDQpUaGlzIGdyYXBoIHNob3dzIHRoZSBjb3JyZWxhdGlvbiB3aXRoIGRlY3JlYXNpbmcgb3JkZXIuDQpgYGB7cn0NCmhyX2VuY29kZWQ8LWFzLmRhdGEuZnJhbWUoaHJfZW5jb2RlZCkNCiMgcmVtb3ZlIGZlYXR1cmVzIHdoaWNoIGFyZSBub3QgdXNlZCBmb3IgY29ycmVsYXRpb24gYW5hbHlzaXMNCnRyYWluaW5nIDwtIHNlbGVjdChocl9lbmNvZGVkLCAtYyhBdHRyaXRpb25fWWVzKSkNCg0KIyBjYWxjdWxhdGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgb2YgZWFjaCBmZWF0dXJlIHdpdGggc3Vydml2YWwNCmZlYXR1cmUgPC0gbmFtZXModHJhaW5pbmcpDQoNCmNvcnJTdXJ2aXZlZCA8LSBkYXRhLmZyYW1lKGZlYXR1cmUgPSBmZWF0dXJlLCBjb2VmID0gcmVwKE5BLCBsZW5ndGgoZmVhdHVyZSkpKQ0KZm9yIChpRmVhdHVyZSBpbiAxOmxlbmd0aChmZWF0dXJlKSl7DQogICAgY29yclN1cnZpdmVkJGNvZWZbaUZlYXR1cmVdIDwtIGNvcih0cmFpbmluZ1ssIGlGZWF0dXJlXSxocl9lbmNvZGVkJEF0dHJpdGlvbl9ZZXMpDQp9DQojIHNvcnQgYnkgY29ycmVsYXRpb24gY29lZmZpY2llbnQNCmNvcnJTdXJ2aXZlZE9yZGVyIDwtIGNvcnJTdXJ2aXZlZFtvcmRlcihjb3JyU3Vydml2ZWQkY29lZiwgZGVjcmVhc2luZyA9IEZBTFNFKSwgXQ0KDQpnZ3Bsb3QoY29yclN1cnZpdmVkT3JkZXIsIGFlcyh4ID0gZmFjdG9yKGZlYXR1cmUsIGxldmVscyA9IGZlYXR1cmUpLCB5ID0gY29lZixmaWxsPWNvZWYpKSArIA0KICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIA0KICAgIGNvb3JkX2ZsaXAoKSArIA0KICAgIHhsYWIoIkZlYXR1cmUiKSArIA0KICAgIHlsYWIoIkNvcnJlbGF0aW9uIENvZWZmaWNpZW50IikNCmBgYA0KY29ycmVsYXRpb24gcGxvdA0KYGBge3J9DQojIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSBwbG90IGZlYXR1cmUgY29ycmVsYXRpb24gbWF0cml4DQpnZXRDb3JyTWF0cml4IDwtIGZ1bmN0aW9uKGZlYXR1cmVMaXN0LCBzaG93UGxvdCA9IFRSVUUpew0KICAgICMgcmVtb3ZlIFN1cnZpdmVkIGZyb20gdHJhaW5pbmcgc2V0IGFuZCBvcmRlciBmZWF0dXJlIHdpdGggcmVzcGVjdCB0byBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCB0byBzdXJ2aXZlZA0KICAgIHBhc3NlbmdlckNvcnIgPC0gaHJfZW5jb2RlZFssIGFzLmNoYXJhY3RlcihmZWF0dXJlTGlzdCldDQogICAgIyBjYWxjdWxhdGUgY29ycmVsYXRpb24gbWF0cml4DQogICAgY29yck1hdHJpeCA8LSBjb3IocGFzc2VuZ2VyQ29ycikNCiAgICAjIHBsb3QgbWF0cml4DQogICAgaWYgKHNob3dQbG90KSB7Y29ycnBsb3QoY29yck1hdHJpeCwgbWV0aG9kID0gImNvbG9yIiwgdHlwZSA9ICJ1cHBlciIpfQ0KICAgIGNvcnJNYXRyaXgNCn0NCg0KY29yck1hdHJpeCA8LSBnZXRDb3JyTWF0cml4KHJldihjb3JyU3Vydml2ZWRPcmRlciRmZWF0dXJlKSkNCmBgYA0KDQpgYGB7cn0NCiMgZnVuY3Rpb24gdG8gZ2V0IGRhdGEgZnJhbWUgd2l0aCBwYWlyd2lzZSBjb3JyZWxhdGlvbiBvZiBmZWF0dXJlcw0KZ2V0UGFpckNvcnJlbGF0aW9uIDwtIGZ1bmN0aW9uKGNvcnJNYXRyaXgpew0KICAgIGZlYXR1cmVOYW1lIDwtIGNvbG5hbWVzKGNvcnJNYXRyaXgpDQogICAgbkZlYXR1cmUgPC0gbGVuZ3RoKGZlYXR1cmVOYW1lKQ0KICAgIA0KICAgICMgc2V0IGxvd2VyIHRyaWFuZ2xlIG9mIG1hdHJpeCB0byBOQSAodGhlc2UgdmFsdWVzIGFyZSBhbGwgcmVkdW5kYW50KQ0KICAgIGNvcnJNYXRyaXhbbG93ZXIudHJpKGNvcnJNYXRyaXgsIGRpYWcgPSBUUlVFKV0gPC0gTkENCiAgICANCiAgICAjIGNvbnZlcnQgbWF0cml4IHRvIGRhdGEgZnJhbWUNCiAgICBmZWF0dXJlUGFpciA8LSBkYXRhLmZyYW1lKGZlYXR1cmUxID0gcmVwKGZlYXR1cmVOYW1lLCBuRmVhdHVyZSksIGZlYXR1cmUyID0gcmVwKGZlYXR1cmVOYW1lLCBlYWNoID0gbkZlYXR1cmUpLCBjb2VmID0gYXMudmVjdG9yKGNvcnJNYXRyaXgpKQ0KICAgICMgcmVtb3ZlIE5Bcw0KICAgIGZlYXR1cmVQYWlyIDwtIGZlYXR1cmVQYWlyWyFpcy5uYShmZWF0dXJlUGFpciRjb2VmKSwgXQ0KICAgICMgY2FsY3VsYXRlIGFic29sdXRlIHZhbHVlIG9mIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50DQogICAgZmVhdHVyZVBhaXIkY29lZkFicyA8LSBhYnMoZmVhdHVyZVBhaXIkY29lZikNCiAgICAjIG9yZGVyIGJ5IGNvZWZmaWNpZW50DQogICAgZmVhdHVyZVBhaXIgPC0gZmVhdHVyZVBhaXJbb3JkZXIoZmVhdHVyZVBhaXIkY29lZkFicywgZGVjcmVhc2luZyA9IFRSVUUpLCBdDQogICAgDQogICAgZmVhdHVyZVBhaXINCn0gICAgDQoNCmZlYXR1cmVDb3JyIDwtIGdldFBhaXJDb3JyZWxhdGlvbihjb3JyTWF0cml4KSANCmBgYA0KDQpMZXRzIGhhdmUgYSBsb29rIGF0IHRoZSB0ZW4gZmVhdHVyZSBwYWlycyB3aXRoIHRoZSBoaWdoZXMgY29ycmVsYXRpb25zOg0KYGBge3J9DQprYWJsZShmZWF0dXJlQ29yclsxOjEwLCBdKSAlPiUga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UpDQpgYGANCmBgYHtyfQ0KIyBwbG90IGhpc3RvZ3JhbSBvZiBjb3JyZWxhdGlvbiBmYWN0b3JzDQpnZ3Bsb3QoZmVhdHVyZUNvcnIsIGFlcyhjb2VmKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMSkgKyB4bGFiKCJDb3JyZWxhdGlvbiBDb2VmZmljaWVudCIpDQoNCmBgYA0KNCBGZWF0dXJlIFJlZHVjdGlvbg0KDQoxKUdyZWVkeSBFbGltaW5hdGlvbg0KYGBge3J9DQojIGZlYXR1cmVzIHRvIGJlIGVsaW1pbmF0ZWQNCm5GZWF0dXJlIDwtIGxlbmd0aChjb3JyU3Vydml2ZWRPcmRlciRmZWF0dXJlKQ0KZmVhdHVyZUVsaW1pbmF0ZSA8LSBjaGFyYWN0ZXIobkZlYXR1cmUpDQoNCiMgZGF0YSBmcmFtZSB3aXRoIGFsbCBmZWF0dXJlcyANCmZlYXR1cmVSZXN0IDwtIGZlYXR1cmVDb3JyDQpmZWF0dXJlUmVzdCRmZWF0dXJlMSA8LSBhcy5jaGFyYWN0ZXIoZmVhdHVyZVJlc3QkZmVhdHVyZTEpDQpmZWF0dXJlUmVzdCRmZWF0dXJlMiA8LSBhcy5jaGFyYWN0ZXIoZmVhdHVyZVJlc3QkZmVhdHVyZTIpDQoNCiMgY2FsY3VsYXRlIGFic29sdXRlIHZhbHVlIG9mIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IHdpdGggYXR0cml0aW9uDQpjb3JyU3Vydml2ZWQkY29lZkFicyA8LSBhYnMoY29yclN1cnZpdmVkJGNvZWYpDQoNCmZvciAoaUZlYXR1cmUgaW4gMToobkZlYXR1cmUgLSAxKSl7DQogICAgIyBnZXQgY29ycmVsYXRpb24gY29lZmZpY2llbnQgdG8gYXR0cml0aW9uDQogICAgY29lZkFiczEgPC0gY29yclN1cnZpdmVkJGNvZWZBYnNbY29yclN1cnZpdmVkJGZlYXR1cmUgPT0gZmVhdHVyZVJlc3QkZmVhdHVyZTFbMV1dDQogICAgY29lZkFiczIgPC0gY29yclN1cnZpdmVkJGNvZWZBYnNbY29yclN1cnZpdmVkJGZlYXR1cmUgPT0gZmVhdHVyZVJlc3QkZmVhdHVyZTJbMV1dDQogICAgDQogICAgIyBjaG9vc2Ugd2hpY2ggZmVhdHVyZSBoYXMgbG93ZXIgYWJzb2x1dGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgdG8gYXR0cml0aW9uDQogICAgaWYgKGNvZWZBYnMxIDw9IGNvZWZBYnMyKSB7DQogICAgICAgICMgZWxpbWluYXRlIGZlYXR1cmUgMQ0KICAgICAgICBmZWF0dXJlUmVtb3ZlIDwtIGZlYXR1cmVSZXN0JGZlYXR1cmUxWzFdDQogICAgICAgIGZlYXR1cmVLZWVwIDwtIGZlYXR1cmVSZXN0JGZlYXR1cmUyWzFdDQogICAgfSBlbHNlIHsNCiAgICAgICAgIyBlbGltaW5hdGUgZmVhdHVyZSAyDQogICAgICAgIGZlYXR1cmVSZW1vdmUgPC0gZmVhdHVyZVJlc3QkZmVhdHVyZTJbMV0NCiAgICAgICAgZmVhdHVyZUtlZXAgPC0gZmVhdHVyZVJlc3QkZmVhdHVyZTFbMV0NCiAgICB9DQogICAgDQogICAgIyBhZGQgc2VsZWN0ZWQgZmVhdHVyZSB0byBlbGltaW5hdGlvbiBsaXN0DQogICAgZmVhdHVyZUVsaW1pbmF0ZVtpRmVhdHVyZV0gPC0gZmVhdHVyZVJlbW92ZQ0KICAgIA0KICAgICMgcmVtb3ZlIGZlYXR1cmUgZnJvbSBmZWF0dXJlUmVzdA0KICAgIGZlYXR1cmVSZXN0IDwtIGZlYXR1cmVSZXN0W2ZlYXR1cmVSZXN0JGZlYXR1cmUxICE9IGZlYXR1cmVSZW1vdmUgJiBmZWF0dXJlUmVzdCRmZWF0dXJlMiAhPSBmZWF0dXJlUmVtb3ZlLCBdDQp9DQoNCiMgYWRkIGxhc3QgcmVtYWluaW5nIGZlYXR1cmUgdG8gZWxpbWluYXRpb24gbGlzdA0KZmVhdHVyZUVsaW1pbmF0ZVtuRmVhdHVyZV0gPC0gZmVhdHVyZUtlZXANCg0KIyByZXZlcnNlIGVsaW1pbmF0aW9uIGxpc3QNCmZlYXR1cmVHRSA8LSByZXYoZmVhdHVyZUVsaW1pbmF0ZSkNCg0KZmVhdHVyZUdFDQpgYGANCg0KUENBIGZvciBmZWF0dXJlIHJlZHVjdGlvbg0KYGBge3J9DQp0cmFpbmluZ01hdHJpeCA8LSBhcy5tYXRyaXgoaHJfZW5jb2RlZCkNCiMgZXhlY3V0ZSBQQ0Egb24gdHJhaW5pbmcgZGF0YQ0KcGNhVHJhaW5pbmcgPC0gc3VtbWFyeShwcmNvbXAodHJhaW5pbmdNYXRyaXgsIHNjYWxlLiA9IFRSVUUpKQ0KcGNhVHJhaW5pbmcNCiMgcGxvdCBjdW1tdWxhdGVkIFIyIHdpdGggcmVzcGVjdCB0byBudW1iZXIgb2YgY29tcG9uZW50cw0KI3Bsb3REYXRhIDwtIGRhdGEuZnJhbWUoY29tcG9uZW50ID0gMTpuRmVhdHVyZSwgUjIgPSBwY2FUcmFpbmluZyRpbXBvcnRhbmNlWzMsIF0pDQpgYGANCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KYGBge3J9DQp5PC1nZ3Bsb3QoaHJbVG90YWxXb3JraW5nWWVhcnMgPiAwICYgVG90YWxXb3JraW5nWWVhcnMgPCAxMCwuTixieT1saXN0KEF0dHJpdGlvbixPdmVyVGltZSldLGFlcyh4PU92ZXJUaW1lLHk9TixmaWxsPUF0dHJpdGlvbikpK2dlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLHdpZHRoID0gMC4yLHBvc2l0aW9uID0gImRvZGdlIikrZ2VvbV90ZXh0KGFlcyhsYWJlbD1OKSwgdmp1c3Q9LTAuMzApK2xhYnMoeCA9Im92ZXJ0aW1lIiwgeSA9ICJOdW1iZXIgb2YgRW1wbG95ZWUiKSt5bGltKDAsNDAwKQ0KZ2dwbG90bHkoeSkNCmBgYA0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQpUaGUgY29uY2x1c2lvbiAsdGhlIG1vc3Qgb2YgdGhlIGVtcGxveWVlcyB3aG8gaGF2ZSB3b3JrZWQgIDAtNSB5ZWFycyAsIHRoZWlyIHNhbGFyeSBpcyBsZXNzIHRoYW4gNTAwMCQsYW5kIGFyZSBkb2luZyBvdmVydGltZSBhbmQgdGhlIGpvYiBsZXZlbCAxIGhhdmUgaGlnaGVyIHByb2JhYmlsdHkgb2YgbGVhdmluZyB0aGUgY29tcGFueS4NCg0KDQpKb2JSb2xlICxBZ2UsIEVudmlyb25tZW50U2F0aXNmYWN0aW9uICxZZWFyc0F0Q29tcGFueSANCmBgYHtyfQ0KeDwtZ2dwbG90KGhyLGFlcyhZZWFyc0F0Q29tcGFueSxNb250aGx5SW5jb21lLGNvbG9yPUVudmlyb25tZW50U2F0aXNmYWN0aW9uLHNoYXBlPUFnZSkpK2dlb21fcG9pbnQoKStmYWNldF93cmFwKH5BdHRyaXRpb24pK2xhYnMoeCA9ICJZZWFycyBBdCB0aGUgQ29tcGFueSIseT0iTW9udGhseV9JbmNvbWUiKQ0KZ2dwbG90bHkoeCkNCmBgYA0KQXMgd2UgY2FuIHNlZSBhYm92ZSB0aGF0IHRoZSBlbXBsb3llZSBvZiAgYWdlIGdyb3VwIDIwLTMwLG51bWJlciBvZiB5ZWFycyBpbiB0aGlzIGNvbXBhbnkgaXMgbGVzcyB0aGFuIDUgIGFuZCBtb250aGx5IHNhbGFyeSBpcyBiZWxvd2UgJDUwMDAgYXJlIGxlYXZpbmcgdGhlIGNvbXBhbnkgdGhlIG1vc3QuIA0KDQoNCk5vdyB3ZSBhcmUga25vd2UgdGhhdCBhdCBqb2IgbGV2ZWwgMSB0aGUgbW9zdCBvZiB0aGUgZW1wbG95ZWUgJ3MgYXJlIGxlYXZpbmcuDQpzbyBub3cgaSBhbSBnb2luZyB0byBjaGVjayBpbiBqb2IgbGV2ZWwgMSBpbiB3aGljaCByb2xlIHRoZSBlbXBsb3llZXMgYXJlIGxlYXZpbmcgdGhlIG1vc3QuDQoNCnBsb3R0aW5nIHRoZSBiYXIgcGxvdCBmb3IgdGhlIGRhdGEgcmVwcmVzZW50YXRpb24uDQpgYGB7cn0NCmdncGxvdChocltKb2JMZXZlbD09MSAmIE1vbnRobHlJbmNvbWU8NTAwMCAmVG90YWxXb3JraW5nWWVhcnM8PTUgJlllYXJzQXRDb21wYW55PDUgJiBBZ2U9PSIyMC0zMCIsLk4sYnk9bGlzdChKb2JSb2xlLEF0dHJpdGlvbixEZXBhcnRtZW50KV0sYWVzKHg9Sm9iUm9sZSx5PU4sZmlsbD1EZXBhcnRtZW50KSkrZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsd2lkdGggPSAwLjIpK2xhYnMoeCA9ImpvYlJvbGUiLCB5ID0gIk51bWJlciBvZiBlbXBsb3llZXMiKStmYWNldF93cmFwKH5BdHRyaXRpb24pK2Nvb3JkX2ZsaXAoKStnZW9tX3RleHQoYWVzKGxhYmVsPU4pLCB2anVzdD0tMC4yKQ0KYGBgDQpmcm9tIHRoaXMgd2UgY2FuIGNvbmNsdWRlIHRoYXQgaW4gam9iIGxldmVsIDEsQWdlIGdyb3VwIGlzIDIwLTMwICxtb250aGx5IGluY29tZSBpcyBsZXNzIHRoYW4gNTAwMCQgLHRvdGFsd29ya2luZyB5ZWFycyBhcmUgbGVzcyB0aGFuIDUgeWVhcnMsYW5kIHllYXJzIGF0IGNvbXBhbnkgbGVzcyB0aGFuIGVxdWFsIHRvIDUgDQpJbiBqb2IgbGV2ZWwgb25lIFRoZXJlIGFyZSB0aHJlZSBkZXBhcnRtZW50DQoxKUh1bWFuIFJlc291cmNlDQoyKVJlc2VhcmNoIGFuZCBkZXZlbG9wbWVudA0KMylzYWxlcy4NCg0KSW4gam9iIGxldmVsIG9uZSB3ZSBoYXZlIDQgam9iIHJvbGVzIGluIGEgRGVwYXJ0bWVudCA6LQ0KMSlMYWJvcmF0b3J5IFRlY2huaWNpYW4NCjIpUmVzZWFyY2ggU2NpZW50aXN0DQozKVNhbGVzIFJlcHJlc2VudGF0aXZlDQo0KUh1bWFuIFJlc291cmNlcyANCg0Kd2hlcmUgdGhlIG1vc3Qgb2YgdGhlIGVtcGxveWVlICdzIGFyZSBsZWF2aW5nIGluIGpvYiByb2xlIG9mIExhYm9yYXRvcnkgVGVjaG5pY2lhbiBvZiBSZXNlYXJjaCBhbmQgZGV2ZWxvcG1lbnQgZGVwYXJ0bWVudCBhbmQgIFNhbGVzIFJlcHJlc2VudGF0aXZlIFNhbGVzIGRlcGFydG1lbnQuDQoNCk1hcml0YWxTdGF0dXMsV29ya0xpZmVCYWxhbmNlIA0KYGBge3J9DQp4PC1nZ3Bsb3QoaHIsYWVzKFdvcmtMaWZlQmFsYW5jZSxNb250aGx5SW5jb21lICxjb2xvcj1NYXJpdGFsU3RhdHVzKSkrZ2VvbV9wb2ludCgpK2ZhY2V0X3dyYXAofkF0dHJpdGlvbikrbGFicyh4ID0gIldvcmtMaWZlQmFsYW5jZSIseT0iTW9udGhseV9JbmNvbWUiKQ0KZ2dwbG90bHkoeCkNCg0KYGBgDQpCeSB0aGlzIGdyYXBoIHdlIGNhbiBjb25jbHVkZSB0aGF0IHRoZSBlbXBsb3llZXMgd2hvICdzIG1vbnRoeSBpbmNvbWUgaXMgbGVzcyB0aGFuIDEwMDAwJCAsTWFyaXRhbCBzdGF0dXMgaXMgc2luZ2xlIGFuZCB3b3JrbGlmZSBiYWxhbmNlIGFyZSBCZXR0ZXIgYXJlIGxlYXZpbmcgdGhlIGNvbXBhbnkuDQoNCg0KYGBge3J9DQoNCmBgYA0KDQo=